#include "modbus-srv.h"

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>

#if defined(_WIN32)
#include <ws2tcpip.h>
#else
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif

#include "modbus/modbus.h"
#include "gige.h" // 设置工业相机属性
#include "util.h"
#include "cJSON.h"

#define __CONFIG_FILE "/mnt/meiteng/config" // 外部可通过modbus-tcp协议来设置

static int s = -1;
static modbus_t *ctx = NULL;
static modbus_mapping_t *mb_mapping = NULL;
static pthread_t tid;
static int server_status = 0;
static int stop_flag = 0;
static char server_host[256] = { 0 };
static int server_port = 0;

void* modbus_entry(void* args)
{
    int rc = 0;
    if (server_host[0] == 0){
        ctx = modbus_new_tcp(NULL, server_port);
    } else {
        ctx = modbus_new_tcp(server_host, server_port);
    }
#ifdef PRINTF_DEBUG
    // modbus_set_debug(ctx,1);
#endif

    s = modbus_tcp_listen(ctx, 1);
    modbus_tcp_accept(ctx, &s);

    server_status = 1;
    while(stop_flag == 0) {
        uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];

        rc = modbus_receive(ctx, query);
        if (rc > 0) {
            modbus_reply(ctx, query, rc, mb_mapping);
        } else if (rc  == -1) {
            __log_mesg("error : %s\n", modbus_strerror(errno));
            if (errno == EMBBADDATA || errno == ETIMEDOUT) {
                continue;
            } else {
                /* Connection closed by the client or error 
                    Accept another client.*/
                __log_mesg("Accept another client.\n");
                modbus_tcp_accept(ctx, &s);
            }
        }
    }
    __log_mesg("modbus slave quit: %s\n", modbus_strerror(errno));
    server_status = 0;
    return 0;
}

#define NB_CONNECTION 5
static int server_socket = -1;

static void close_sigint(int dummy)
{
    if (server_socket != -1) {
        close(server_socket);
    }
    modbus_free(ctx);
    modbus_mapping_free(mb_mapping);

    exit(dummy);
}

/**
 * @brief The caller is required to free the root pointer
 * 
 * @param root 
 */
void _save_config(cJSON* root)
{
    char *tmp = NULL;
    char *data = NULL;
    int data_len;
    tmp = cJSON_Print(root);
    //printf("after update \n%s\n", tmp);

    data_len = strlen(tmp);
    data = (char *)malloc(data_len + 2); // data_len + '\n' + '\0'

    strncpy(data, tmp, data_len);
    data[data_len] = '\n';
    data[data_len + 1] = '\0';

    __write_file(__CONFIG_FILE, data, data_len + 1);
    free(tmp);
    free(data);
}

void _update_config(uint16_t addr, int type, modbus_mapping_t* map)
{
    cJSON* root = NULL;
    char* data = NULL;
    data = __read_file(__CONFIG_FILE);
    if (!data) {
        __log_mesg("Config file dont exist.\n");
        return;  
    }
    root = cJSON_Parse(data);
    if (!root) {
        __log_mesg("Parse Json Error! Error before: [%s]\n", cJSON_GetErrorPtr());
        free(data);
        return;
    }
    cJSON* config = cJSON_GetObjectItem(root, "config");
    cJSON *item = NULL;
    int array_size = 0;
    int i = 0;

    array_size = cJSON_GetArraySize(config);

    for (i = 0; i < array_size; ++i) {
        item = cJSON_GetArrayItem(config, i);
        if (cJSON_GetObjectItem(item, "type")->valueint == type &&
            cJSON_GetObjectItem(item, "addr")->valueint == addr) {
                __log_mesg("Update tab_registers[%d] to %d\n", addr, map->tab_registers[addr]);
                // number类型需要同时更新valueint和valuedouble
                cJSON_GetObjectItem(item, "value")->valueint = map->tab_registers[addr];
                cJSON_GetObjectItem(item, "value")->valuedouble = map->tab_registers[addr];
                break;
        }
    }

    _save_config(root);
    if (root) cJSON_Delete(root);    
    if (data) free(data);
}

/**
 * @brief 收到0x06时，设置对应设备
 * 
 * @param addr 
 */
void _set_single_gige_property(uint16_t addr)
{
    if (!gige_is_opened()) return;
    switch(addr){
        case 0x01:
            gige_set_fps(mb_mapping->tab_registers[addr]);
            break;
        case 0x02:
            gige_set_exposure(mb_mapping->tab_registers[addr]);
            break;
        case 0x03:
            gige_set_external_trigger_mode(mb_mapping->tab_registers[addr]);
            break;
        default:
            break;
    }
}

void set_all_gige_propertise()
{
    if (!get_gige_connected() || !gige_is_opened()) return;
    int n1 = gige_set_fps(mb_mapping->tab_registers[GIGE_FPS_ADDR]);
    int n2 = gige_set_exposure(mb_mapping->tab_registers[GIGE_EXPOSURE_ADDR]);
    int n3 = gige_set_external_trigger_mode(mb_mapping->tab_registers[GIGE_EXTERNAL_TRIGGER_MODE_ADDR]);
    if (!n1 && !n2 && !n3) {
        __log_mesg("Set gige property => fps:%d exposure:%d ex-tr-mode:%d\n",
        mb_mapping->tab_registers[GIGE_FPS_ADDR],
        mb_mapping->tab_registers[GIGE_EXPOSURE_ADDR],
        mb_mapping->tab_registers[GIGE_EXTERNAL_TRIGGER_MODE_ADDR]);
    } else {
        __log_mesg("Set gige property failed.\n");
    }
}

void _load_config_from_file(cJSON* array, modbus_mapping_t* map)
{
    cJSON *item = NULL;
    int array_size = 0;
    int i = 0;

    array_size = cJSON_GetArraySize(array);

    int addr = 0,value = 0,type = 0;
    for (i = 0; i < array_size; ++i) {
        item = cJSON_GetArrayItem(array, i);
        addr = cJSON_GetObjectItem(item, "addr")->valueint;
        value = cJSON_GetObjectItem(item, "value")->valueint;
        type = cJSON_GetObjectItem(item, "type")->valueint;
        if (type == 6) {
            map->tab_registers[addr] = value;
        }       
    }
}

/**
 * @brief 加载配置。
 * 如果程序第一次运行无配置文件，相机配置从相机中读入，其他配置默认为0.并存储
 * 
 * @param mb_mapping 
 */
void _load_config(modbus_mapping_t* mb_mapping)
{
    char* data = NULL;
    cJSON* root = NULL;
    data = __read_file(__CONFIG_FILE);
    if (!data) {
        __log_mesg("Read %s fail\n", __CONFIG_FILE); 
        goto end;
    }

    root = cJSON_Parse(data);
    if (!root) {
        __log_mesg("Parse Json Error! Error before: [%s]\n", cJSON_GetErrorPtr());
        goto end;
    }

    _load_config_from_file(cJSON_GetObjectItem(root,"config"), mb_mapping);
    
    //set_all_gige_propertise(mb_mapping);
end:
    if (root) cJSON_Delete(root);
    if (data) free(data);
}

void* modbus_entry_select(void* args)
{
    uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
    int master_socket;
    int rc;
    fd_set refset;
    fd_set rdset;
    /* Maximum file descriptor number */
    int fdmax;
    
    if (server_host[0] == 0){
        ctx = modbus_new_tcp(NULL, server_port);
    } else {
        ctx = modbus_new_tcp(server_host, server_port);
    }
#ifdef PRINTF_DEBUG
    //modbus_set_debug(ctx,1);
#endif

    server_socket = modbus_tcp_listen(ctx, NB_CONNECTION);
    if (server_socket == -1) {
        __log_mesg("Unable to listen TCP connection\n");
        modbus_free(ctx);
        return NULL;
    }

    signal(SIGINT, close_sigint);

    /* Clear the reference set of socket */
    FD_ZERO(&refset);
    /* Add the server socket */
    FD_SET(server_socket, &refset);

    /* Keep track of the max file descriptor */
    fdmax = server_socket;

    for (;;) {
        rdset = refset;
        /*select正常返回三个事件中set的fd数量
        * 返回0 表示超时
        * 返回-1 表示出错
        */
        if (select(fdmax+1, &rdset, NULL, NULL, NULL) == -1) {
            __log_mesg("Server select() failure.");
            close_sigint(1);
        }

        /* Run through the existing connections looking for data to be
         * read */
        for (master_socket = 0; master_socket <= fdmax; master_socket++) {

            if (!FD_ISSET(master_socket, &rdset)) {
                continue;
            }

            if (master_socket == server_socket) {
                /* A client is asking a new connection */
                socklen_t addrlen;
                struct sockaddr_in clientaddr;
                int newfd;

                /* Handle new connections */
                addrlen = sizeof(clientaddr);
                memset(&clientaddr, 0, sizeof(clientaddr));
                newfd = accept(server_socket, (struct sockaddr *)&clientaddr, &addrlen);
                if (newfd == -1) {
                    __log_mesg("modbus-srv accept() error=>%s\n", strerror(errno));
                } else {
                    FD_SET(newfd, &refset);
                    if (newfd > fdmax) {
                        /* Keep track of the maximum */
                        fdmax = newfd;
                    }
                    __log_mesg("New slave connection from %s:%d on socket %d\n",
                           inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port, newfd);
                }
            } else {
                modbus_set_socket(ctx, master_socket);
                rc = modbus_receive(ctx, query);
                if (rc > 0) {
                    int offset = 7;
                    modbus_reply(ctx, query, rc, mb_mapping);
                    int function = query[offset];
                    if (function == MODBUS_FC_WRITE_SINGLE_REGISTER) {
                        uint16_t addr = (query[offset + 1] << 8) + query[offset + 2];
                        //更新配置
                        _update_config(addr, function, mb_mapping); 
                        //设置相机属性
                        _set_single_gige_property(addr);
                    }

                } else if (rc == -1) {
                    //if (errno == EAGAIN) {
                    //    continue;
                    //}
                    /* This example server in ended on connection closing or
                     * any errors. */
                    __log_mesg("Connection closed on socket %d=>%s\n", master_socket, strerror(errno));
                    close(master_socket);

                    /* Remove from reference set */
                    FD_CLR(master_socket, &refset);

                    if (master_socket == fdmax) {
                        fdmax--;
                    }
                }
            }
        }
    }
    __log_mesg("modbus-srv error. exit.\n");
    exit(-1);
    return 0;
}

int modbus_start(const char* host, int port)
{
    int err;
    if (host != NULL) {
        __log_mesg("Modbus slave listen on %s", host);
    }
    server_port = port;
    stop_flag = 0;

    mb_mapping = modbus_mapping_new(0, 0,  /* nb_bits , nb_input_bits */
                                    MODBUS_MAX_WRITE_REGISTERS, MODBUS_MAX_READ_REGISTERS); /* nb_registers , nb_input_registers*/
    if (mb_mapping == NULL) {
        __log_mesg("Failed to allocate the mapping: %s\n",modbus_strerror(errno));
        modbus_free(ctx);
        return EXIT_FAILURE;
    }

    // 加载配置
    _load_config(mb_mapping);
    __log_mesg("Load config successful.\n");
	err = pthread_create(&tid, NULL, modbus_entry_select, 0);
	if (err != 0)
	{
		__log_mesg("Failed to create modbus server working thread.\n");
        exit(-1);
		return EXIT_FAILURE;
	}
    __log_mesg("Modbus-tcp slave started.\n");
	return EXIT_SUCCESS;
}


int get_holding_register_uint16(int address) 
{
    if (address >= MODBUS_MAX_WRITE_REGISTERS) {
        __log_mesg("Holding register address out of range.\n");
        return -1;
    }
    return (int)(mb_mapping->tab_registers[address]);
}

void set_holding_register_uint16(uint16_t value, int address)
{
    if (address >= MODBUS_MAX_WRITE_REGISTERS) {
        __log_mesg("Holding register address out of range.\n");
        return;
    }
    mb_mapping->tab_registers[address] = value;
}

void set_input_register_uint16(uint16_t value, int address)
{
    if (address >= MODBUS_MAX_READ_REGISTERS) {
        __log_mesg("Input register address out of range.\n");
        return;
    }
    if (mb_mapping != NULL){
        mb_mapping->tab_input_registers[address] = value;
    }
}

void set_input_register_float_dcba(float value, int address) 
{
    if (address >= MODBUS_MAX_READ_REGISTERS) {
        __log_mesg("Input register address out of range.\n");
        return;
    }
    modbus_set_float_dcba(value, &(mb_mapping->tab_input_registers[address]));
}

int modbus_stop()
{
    if (server_status == 0){
        return EXIT_SUCCESS;
    }
    stop_flag = 1;
    pthread_join(tid, NULL);
    modbus_mapping_free(mb_mapping);
    if (s != -1) {
        close(s);
    }
    /* For RTU, skipped by TCP (no TCP connect) */
    modbus_close(ctx);
    modbus_free(ctx);

    return EXIT_SUCCESS;
}

int request_stop_signal()
{
    return mb_mapping->tab_registers[STOP_SIGNAL_ADDR];
}

void set_tarpa_alarm()
{
    set_input_register_uint16(1, TIE_TARPAULIN_STATUS_ADDR);
}

void reset_tarpa_alarm()
{
    set_input_register_uint16(0, TIE_TARPAULIN_STATUS_ADDR);
}

void fill_tarpa_alarm()
{
    set_input_register_uint16(20, TIE_TARPAULIN_STATUS_ADDR);
}

void set_spray_alarm()
{
    set_input_register_uint16(1, TIE_SPRAYER_STATUS_ADDR);
}

void reset_spray_alarm()
{
    set_input_register_uint16(0, TIE_SPRAYER_STATUS_ADDR);
}

void fill_spray_alarm()
{
    set_input_register_uint16(20, TIE_SPRAYER_STATUS_ADDR);
}

void set_beam_signal()
{
    mb_mapping->tab_registers[BEAM_SIGNAL_ADDR] = 1;
}

int is_beam_signal_set()
{
    return mb_mapping->tab_registers[BEAM_SIGNAL_ADDR];
}

void reset_beam_signal()
{
    mb_mapping->tab_registers[BEAM_SIGNAL_ADDR] = 0;
}

int get_beam_delay_time()
{
    return mb_mapping->tab_registers[BEAM_DELAY_TIME_ADDR];
}

void set_gige_normal()
{
    mb_mapping->tab_input_registers[TIE_GIGE_EXCEPTION_ADDR] = 0;
}

void set_gige_exception()
{
    mb_mapping->tab_input_registers[TIE_GIGE_EXCEPTION_ADDR] = 1;
}

int get_gige_connected()
{
    return mb_mapping->tab_registers[GIGE_CONNECTED];
}